#pragma once
#include "gtest/gtest.h"
#include "DBManager/DBServiceFactory.h"
#include "DBManager/IService/Input/Subcase/ISubcaseService.h"
#include "DBManager/IService/Input/Coordinate/ICORD2RService.h"
#include "DBManager/IService/Input/Coordinate/ICORD2CService.h"
#include "DBManager/IService/Input/Coordinate/ICORD2SService.h"
#include "SGFem/Pre/Assembly/Assembly.h"
#include "SGFem/Pre/Assembly/CoordinateProcessor.h"
#include "../../Factory/ElementCalculatorFactory.h"
#include "../Conm2ElementCalculator.h"

using SG::Algebra::Matrixd;
using namespace SG::DataStructure::FEM;
using namespace SG::DBManager;
using SG::FEMSolver::AssemblyEigen;
using SG::FEMSolver::CoordinateProcessor;
using namespace SG::FEMSolver::Element;

// ElementCalculatorFactory 是否正确创建 Conm2 单元计算类
TEST (ElementCalculatorFactoryTest, CreateConm2Calculator)
{
    ElementCalculatorFactory::Register (ElementTypes::ELETYPE_Conm2, Conm2ElementCalculator::Create);

    // Test data
    const auto calculatorPtr = ElementCalculatorFactory::Create (ElementTypes::ELETYPE_Conm2);
    const auto conm2CalculatorPtr = dynamic_cast<Conm2ElementCalculator*> (calculatorPtr.get ());

    // Check
    ASSERT_TRUE (nullptr != conm2CalculatorPtr);
}

extern std::shared_ptr<DBServiceFactory> DB_SERVICEFACTORY;
class Conm2ElementCalculatorTest : public ::testing::Test
{
protected:
    static void SetUpTestSuite ()
    {
        DB_SERVICEFACTORY->ClearDatabase ();

        // 直角坐标系 100
        CORD2RData rectangularCoordSys{100, 0, {1.0, 2.0, 3.0}, {2.0, 4.0, 1.0}, {5.0, 1.0, 1.0}};
        DB_SERVICEFACTORY->get<ICORD2RService> ()->Add (rectangularCoordSys);

        // 直角坐标系 101
        rectangularCoordSys = {101, 100, {4.0, 3.0, 7.0}, {2.0, 6.0, 1.0}, {5.0, 1.0, 4.0}};
        DB_SERVICEFACTORY->get<ICORD2RService> ()->Add (rectangularCoordSys);

        // 柱坐标系 200
        CORD2CData cylindricalCoordSys{200, 0, 1.0, 2.0, 3.0, 2.0, 4.0, 1.0, 5.0, 1.0, 1.0};
        DB_SERVICEFACTORY->get<ICORD2CService> ()->Add (cylindricalCoordSys);

        // 柱坐标系 201
        cylindricalCoordSys = {201, 200, 4.0, 3.0, 7.0, 2.0, 6.0, 1.0, 5.0, 1.0, 4.0};
        DB_SERVICEFACTORY->get<ICORD2CService> ()->Add (cylindricalCoordSys);

        // 球坐标系 300
        CORD2SData sphericalCoordSys{300, 0, 1.0, 2.0, 3.0, 2.0, 4.0, 1.0, 5.0, 1.0, 1.0};
        DB_SERVICEFACTORY->get<ICORD2SService> ()->Add (sphericalCoordSys);

        // 球坐标系 301
        sphericalCoordSys = {301, 300, 4.0, 3.0, 7.0, 2.0, 6.0, 1.0, 5.0, 1.0, 4.0};
        DB_SERVICEFACTORY->get<ICORD2SService> ()->Add (sphericalCoordSys);

        // 生成节点排序
        auto& assembly = AssemblyEigen::GetInstance ();
        assembly.Clear ();
        Subcase subcase;
        assembly.Initialize (DB_SERVICEFACTORY, subcase, {1});

        // 计算坐标系原点和基矢量
        auto& coordProcessor = CoordinateProcessor::GetInstance ();
        coordProcessor.Clear ();
        coordProcessor.Initialize (DB_SERVICEFACTORY);
    }

    /**
     * @brief 每个测试都重置单元数据
     * @date  2024.11.19
     */
    void SetUp () override
    {
        node = {2, 0, {15.0, 10.0, 5.0}, 0};

        element = {2, 2, 0, 100.0, {10.0, 20.0, 30.0}, {200.0}, {10.0, 300.0}, {10.0, 20.0, 400.0}};
    }

    NodeData node;
    CONM2Data element;
};

TEST_F (Conm2ElementCalculatorTest, ComputeLocalMass)
{
    // Input data
    element.m_x[0] = 0.0;
    element.m_x[1] = 0.0;
    element.m_x[2] = 0.0;

    // Test data
    Matrixd massTest (6, 6);
    Conm2ElementCalculator::ComputeMass (element, node, _OUT massTest);

    // Target data
    const Matrixd massTarget (6, 6, {
    1.00000000E+02, 0.00000000E+00, 0.00000000E+00,  0.00000000E+00,  0.00000000E+00,  0.00000000E+00,
    0.00000000E+00, 1.00000000E+02, 0.00000000E+00,  0.00000000E+00,  0.00000000E+00,  0.00000000E+00,
    0.00000000E+00, 0.00000000E+00, 1.00000000E+02,  0.00000000E+00,  0.00000000E+00,  0.00000000E+00,
    0.00000000E+00, 0.00000000E+00, 0.00000000E+00,  2.00000000E+02, -1.00000000E+01, -1.00000000E+01,
    0.00000000E+00, 0.00000000E+00, 0.00000000E+00, -1.00000000E+01,  3.00000000E+02, -2.00000000E+01,
    0.00000000E+00, 0.00000000E+00, 0.00000000E+00, -1.00000000E+01, -2.00000000E+01,  4.00000000E+02
    });

    // Check
    ASSERT_TRUE (massTarget == massTest);
}

TEST_F (Conm2ElementCalculatorTest, ComputeLocalMassWithOffset)
{
    // Test data
    Matrixd massTest (6, 6);
    Conm2ElementCalculator::ComputeMass (element, node, _OUT massTest);

    // Target data
    const Matrixd massTarget (6, 6, {
     1.00000000E+02,  0.00000000E+00,  0.00000000E+00,  0.00000000E+00,  3.00000000E+03, -2.00000000E+03,
     0.00000000E+00,  1.00000000E+02,  0.00000000E+00, -3.00000000E+03,  0.00000000E+00,  1.00000000E+03,
     0.00000000E+00,  0.00000000E+00,  1.00000000E+02,  2.00000000E+03, -1.00000000E+03,  0.00000000E+00,
     0.00000000E+00, -3.00000000E+03,  2.00000000E+03,  1.30200000E+05, -2.00100000E+04, -3.00100000E+04,
     3.00000000E+03,  0.00000000E+00, -1.00000000E+03, -2.00100000E+04,  1.00300000E+05, -6.00200000E+04,
    -2.00000000E+03,  1.00000000E+03,  0.00000000E+00, -3.00100000E+04, -6.00200000E+04,  5.04000000E+04
    });

    // Check
    ASSERT_TRUE (massTarget == massTest);
}

TEST_F (Conm2ElementCalculatorTest, ComputeLocalMassWithOffsetPoint)
{
    // Input data
    element.m_cId = -1;

    // Test data
    Matrixd massTest (6, 6);
    Conm2ElementCalculator::ComputeMass (element, node, _OUT massTest);

    // Target data
    const Matrixd massTarget (6, 6, {
     1.00000000E+02,  0.00000000E+00,  0.00000000E+00,  0.00000000E+00,  2.50000000E+03, -1.00000000E+03,
     0.00000000E+00,  1.00000000E+02,  0.00000000E+00, -2.50000000E+03,  0.00000000E+00, -5.00000000E+02,
     0.00000000E+00,  0.00000000E+00,  1.00000000E+02,  1.00000000E+03,  5.00000000E+02,  0.00000000E+00,
     0.00000000E+00, -2.50000000E+03,  1.00000000E+03,  7.27000000E+04,  4.99000000E+03,  1.24900000E+04,
     2.50000000E+03,  0.00000000E+00,  5.00000000E+02,  4.99000000E+03,  6.53000000E+04, -2.50200000E+04,
    -1.00000000E+03, -5.00000000E+02,  0.00000000E+00,  1.24900000E+04, -2.50200000E+04,  1.29000000E+04
    });

    // Check
    ASSERT_TRUE (massTarget == massTest);
}

TEST_F (Conm2ElementCalculatorTest, ComputeGlobalMassWithRCPRCDRCIDgt0Offset)
{
    // Input data
    node.m_defCoord  = 100;
    node.m_dispCoord = 101;
    element.m_cId    = 100;

    // Test data
    Matrixd massTest (6, 6);
    Conm2ElementCalculator::ComputeMass (element, node, _OUT massTest);

    // Target data
    const Matrixd massTarget (6, 6, {
     1.00000000E+02, -2.66453526E-15,  0.00000000E+00, -1.13686838E-13, -2.00000000E+03,  1.73500401E+03,
    -2.66453526E-15,  1.00000000E+02, -9.32587341E-15,  2.00000000E+03, -8.52651283E-14, -2.64381563E+03,
     0.00000000E+00, -9.32587341E-15,  1.00000000E+02, -1.73500401E+03,  2.64381563E+03, -1.13686838E-13,
    -1.13686838E-13,  2.00000000E+03, -1.73500401E+03,  7.04069882E+04, -4.58507265E+04, -5.28310997E+04,
    -2.00000000E+03, -8.52651283E-14,  2.64381563E+03, -4.58507265E+04,  1.10115461E+05, -3.47422750E+04,
     1.73500401E+03, -2.64381563E+03, -1.13686838E-13, -5.28310997E+04, -3.47422750E+04,  1.00377551E+05
    });

    // Check
    ASSERT_TRUE (massTarget == massTest);
}

TEST_F (Conm2ElementCalculatorTest, ComputeGlobalMassWithCCPCCDCCIDgt0Offset)
{
    // Input data
    node.m_defCoord  = 200;
    node.m_dispCoord = 201;
    element.m_cId    = 200;

    // Test data
    Matrixd massTest (6, 6);
    Conm2ElementCalculator::ComputeMass (element, node, _OUT massTest);

    // Target data
    const Matrixd massTarget (6, 6, {
     1.00000000E+02, -4.88498131E-15,  7.10542736E-15,  0.00000000E+00, -3.04746256E+03,  2.16742127E+03,
    -4.88498131E-15,  1.00000000E+02,  2.66453526E-15,  3.04746256E+03, -1.42108547E-14,  1.23519122E+02,
     7.10542736E-15,  2.66453526E-15,  1.00000000E+02, -2.16742127E+03, -1.23519122E+02,  0.00000000E+00,
     0.00000000E+00,  3.04746256E+03, -2.16742127E+03,  1.40072400E+05,  2.68037791E+03,  3.83219394E+03,
    -3.04746256E+03, -1.42108547E-14, -1.23519122E+02,  2.68037791E+03,  9.33211684E+04, -6.60657708E+04,
     2.16742127E+03,  1.23519122E+02,  0.00000000E+00,  3.83219394E+03, -6.60657708E+04,  4.75064315E+04
    });

    // Check
    ASSERT_TRUE (massTarget == massTest);
}

TEST_F (Conm2ElementCalculatorTest, ComputeGlobalMassWithSCPSCDSCIDgt0Offset)
{
    // Input data
    node.m_defCoord  = 300;
    node.m_dispCoord = 301;
    element.m_cId    = 300;

    // Test data
    Matrixd massTest (6, 6);
    Conm2ElementCalculator::ComputeMass (element, node, _OUT massTest);

    // Target data
    const Matrixd massTarget (6, 6, {
     1.00000000E+02,  6.78580456E-15, -4.02455846E-15,  1.70974346E-14, -3.10057050E+03,  1.79121168E+03,
     6.78580456E-15,  1.00000000E+02, -2.66453526E-15,  3.10057050E+03,  4.26325641E-14,  1.08536781E+03,
    -4.02455846E-15, -2.66453526E-15,  1.00000000E+02, -1.79121168E+03, -1.08536781E+03,  7.10542736E-15,
     1.70974346E-14,  3.10057050E+03, -1.79121168E+03,  1.28419094E+05,  1.94461766E+04,  3.36638798E+04,
    -3.10057050E+03,  4.26325641E-14, -1.08536781E+03,  1.94461766E+04,  1.08218656E+05, -5.55624963E+04,
     1.79121168E+03,  1.08536781E+03,  7.10542736E-15,  3.36638798E+04, -5.55624963E+04,  4.42622496E+04
    });

    // Check
    ASSERT_TRUE (massTarget == massTest);
}

TEST_F (Conm2ElementCalculatorTest, ComputeGlobalMassWithRCPRCDOffsetPoint)
{
    // Input data
    node.m_defCoord  = 100;
    node.m_dispCoord = 101;
    element.m_cId    = -1;

    // Test data
    Matrixd massTest (6, 6);
    Conm2ElementCalculator::ComputeMass (element, node, _OUT massTest);

    // Target data
    const Matrixd massTarget (6, 6, {
     1.00000000E+02,  7.10542736E-15, -7.10542736E-15, -2.27373675E-13, -5.09639866E+02, -4.01820796E+03,
     7.10542736E-15,  1.00000000E+02,  5.32907052E-15,  5.09639866E+02, -1.13686838E-13,  2.73231668E+03,
    -7.10542736E-15,  5.32907052E-15,  1.00000000E+02,  4.01820796E+03, -2.73231668E+03,  1.13686838E-13,
    -2.27373675E-13,  5.09639866E+02,  4.01820796E+03,  1.64398327E+05, -1.09741888E+05,  1.40058296E+04,
    -5.09639866E+02, -1.13686838E-13, -2.73231668E+03, -1.09741888E+05,  7.75519424E+04,  2.04692111E+04,
    -4.01820796E+03,  2.73231668E+03,  1.13686838E-13,  1.40058296E+04,  2.04692111E+04,  2.36375381E+05
    });

    // Check
    ASSERT_TRUE (massTarget == massTest);
}